/* cli.c --- 
 * 
 * @file cli.c
 * @brief 
 * @author Molnár Zoltán
 * @date Sat Nov 23 17:08:25 2013 (+0100)
 * @version: 
 * Last-Updated: Sun Feb  2 21:27:20 2014 (+0100)
 *           By: Molnár Zoltán
 * 
 */

/* This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 */

/*******************************************************************************/
/* INCLUDES                                                                    */
/*******************************************************************************/
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_gpio.h"
#include "misc.h"
/* FreeRTOS */
#include <FreeRTOS.h>
#include <semphr.h>

#include <math.h>
#include <string.h>

/*******************************************************************************/
/* DEFINED CONSTANTS                                                           */
/*******************************************************************************/

/*******************************************************************************/
/* MACRO DEFINITIONS                                                           */
/*******************************************************************************/
/** USART1 busy-wait for flag to be set */
#define USART1_WAIT(flag)                                       \
  do                                                            \
    {                                                           \
      while( SET != USART_GetFlagStatus( USART1, (flag)));      \
    }                                                           \
  while(0)


/*******************************************************************************/
/* TYPE DEFINITIONS                                                            */
/*******************************************************************************/


/*******************************************************************************/
/* DEFINITIONS OF GLOBAL CONSTANTS AND VARIABLES                               */
/*******************************************************************************/


/*******************************************************************************/
/* DEFINITIONS OF LOCAL CONSTANTS AND VARIABLES                                */
/*******************************************************************************/
/** Semaphore for synchronisation */
static xSemaphoreHandle LOC_UsartTxSem = NULL;
static xSemaphoreHandle LOC_UsartRxSem = NULL;
static xSemaphoreHandle LOC_CliTxMutex = NULL;
static xSemaphoreHandle LOC_CliRxMutex = NULL;

/** Pointer to the data to send */
static unsigned char *LOC_pucTxMsg = NULL;

/** Number of bytes to send */
static uint32_t LOC_u32NumOfBytesToSend = 0;

/** Number of bytes actually sent */
static uint32_t LOC_u32TxCounter = 0;

/** Pointer to the buffer to store incoming data in */
static unsigned char *LOC_pucRxMsg = NULL;

/** Number of bytes to receive */
static uint32_t LOC_u32NumOfBytesToReceive = 0;

/** Number of bytes actually received */
static uint32_t LOC_u32RxCounter = 0;


/*******************************************************************************/
/* DEFINITION OF LOCAL FUNCTIONS                                               */
/*******************************************************************************/
/**
 * Configure RCC 
 */
static void LOC_vidInitRcc( void)
{
  /* Enable clock */
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE);

}

/**
 * Initialize GPIO pins for USART
 */
static void LOC_vidInitGpioPins( void)
{
  GPIO_InitTypeDef tstrGPIO_InitStructure;

  /* Configure USART1 Rx PA10 as input floating */
  tstrGPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  tstrGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  tstrGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init( GPIOA, &tstrGPIO_InitStructure);

  /* Configure USART1 Tx PA9 as alternate function push-pull */
  tstrGPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  tstrGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  tstrGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init( GPIOA, &tstrGPIO_InitStructure);  
}

/**
 * Initialize USART
 */
static void LOC_vidInitUsart( void)
{
  USART_InitTypeDef tstrUSART_InitStructure;

  tstrUSART_InitStructure.USART_BaudRate = 9600;
  tstrUSART_InitStructure.USART_WordLength = USART_WordLength_8b;
  tstrUSART_InitStructure.USART_StopBits = USART_StopBits_1;
  tstrUSART_InitStructure.USART_Parity = USART_Parity_No;
  tstrUSART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  tstrUSART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  


  /* Configure USART1 */
  USART_Init(USART1, &tstrUSART_InitStructure);

  /* Enable USART1 Receive interrupt */
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

  /* Enable USART1 interrupt in NVIC */
  NVIC_EnableIRQ(USART1_IRQn);

  /* Enable the USART1 */
  USART_Cmd( USART1, ENABLE);
}

/**
 * Send string via USART1
 */
static uint32_t LOC_vidUsartSendString( char *pcStr, uint32_t u32Len)
{
  /* Setup transfer parameters */
  LOC_pucTxMsg = pcStr;
  LOC_u32NumOfBytesToSend = u32Len;
  LOC_u32TxCounter = 0;

  /* Send first byte */
  USART_SendData( USART1, LOC_pucTxMsg[ LOC_u32TxCounter++]);

  /* Enable USART1 Transmit interrupt */
  USART_ITConfig(USART1, USART_IT_TXE, ENABLE);

  /* Wait transfer to finish let ISR do the job */
  xSemaphoreTake( LOC_UsartTxSem, portMAX_DELAY);
  
  /* Return the number of bytes sent */
  return LOC_u32TxCounter;
} 

/*******************************************************************************/
/* DEFINITION OF GLOBAL FUNCTIONS                                              */
/*******************************************************************************/
/**
 * Initialize CLI port
 */
void CLI_vidInit( void)
{
  LOC_vidInitRcc();
  LOC_vidInitGpioPins();
  LOC_vidInitUsart();

  /* Create semaphores for synchronisation */
  LOC_UsartTxSem = xSemaphoreCreateBinary( );

  if( NULL == LOC_UsartTxSem)
    /* Error, semaphore was not created */
    for(;;);

  LOC_UsartRxSem = xSemaphoreCreateBinary( );

  if( NULL == LOC_UsartRxSem)
    /* Error, semaphore was not created */
    for(;;);

  LOC_CliTxMutex = xSemaphoreCreateBinary( );

  if( NULL == LOC_CliTxMutex)
    /* Error, mutex was not created */
    for(;;);

  xSemaphoreGive( LOC_CliTxMutex);

  /* The initial value of the semaphore must be 1 */
  LOC_CliRxMutex = xSemaphoreCreateBinary( );

  if( NULL == LOC_CliRxMutex)
    /* Error, mutex was not created */
    for(;;);

}

/**
 * Task to manage CLI
 */
void vTaskCli( void *pvParameters)
{
  uint32_t u32i = 0;

  /* As per most tasks, this task is implemented in an infinite loop. */
  for( ;; )
    {
      xSemaphoreTake( LOC_UsartRxSem, portMAX_DELAY);
    }
}

/**
 *
 */
void CLI_vidSendString( unsigned char *pcMsg, uint32_t u32Length)
{
  /* Take care of mutual exclusion */
  xSemaphoreTake( LOC_CliTxMutex, portMAX_DELAY);
  LOC_vidUsartSendString( pcMsg, u32Length);
  xSemaphoreGive( LOC_CliTxMutex);
}

/**
 * Interrupt handler of USART
 */
void USART1_ISR( void)
{
  /* In case of receive interrupt */
  if( USART_GetITStatus( USART1, USART_IT_RXNE) != RESET)
    {
      /* Echo received character to the sender */
      char cData = USART_ReceiveData( USART1);      
      
      USART_SendData( USART1, cData);
      USART1_WAIT( USART_FLAG_TXE);

      /* Send newline character as well (if needed) 
         and wake-up sleeping process as command has been received*/
      if( '\r' == cData)
        {
          USART_SendData( USART1, '\n');
          USART1_WAIT( USART_FLAG_TXE);
          /* Wake-up waiting process */
          xSemaphoreGiveFromISR( LOC_UsartRxSem, NULL);
        }
    }

  /* In case of transmit interrupt */
  if( USART_GetITStatus( USART1, USART_IT_TXE) != RESET)
    {
      if( LOC_u32TxCounter < LOC_u32NumOfBytesToSend)
        {
          USART_SendData( USART1, LOC_pucTxMsg[LOC_u32TxCounter++]);
        }
      else
        {
          /* Reset control variables */
          LOC_pucTxMsg = NULL;
          LOC_u32NumOfBytesToSend = 0;
          LOC_u32TxCounter = 0;

          /* Disable the USART1 Transmit interrupt */
          USART_ITConfig( USART1, USART_IT_TXE, DISABLE);

          /* Let user process to wake up */
          xSemaphoreGiveFromISR( LOC_UsartTxSem, NULL);      
        }
    }

  
}

/******************************* END OF FILE ***********************************/

